use bcrypt::DEFAULT_COST;
use chrono::NaiveDate;
use entity::{
    booking, booking_status,
    meeting_room::{self, EntityTrait},
    sea_orm::{ConnectionTrait, Set},
    user::{admin, auth, custom},
};
use rand::Rng;
use sea_orm_migration::prelude::*;

pub struct Migration;

impl MigrationName for Migration {
    fn name(&self) -> &str {
        "m20220521_204511_radom_data"
    }
}

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        let db = manager.get_connection();
        //add admin
        new_admin(db, "123456", "admin").await?;

        if cfg!(debug_assertions) {
            // add user
            let cid = new_custom(db, "123456", "custom").await?;

            let mut ids = vec![];

            let range: u64 =
                0b_1111_1111_1100_0000_0000_0000_1111_1111_0000_0000_0000_0000_0000_0011_1111_1111;
            let book: u64 =
                0b_0000_0000_0000_1111_1100_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
            let ibook: i64 = unsafe { *(&book as *const u64 as *const i64) };

            let irange = range as i64;
            println!("{range:064b} | {irange:064b}");
            println!("{book:064b} | {ibook:064b}");
            println!("{book:064b} | {ibook:064b}", book = book >> 20);

            let date = NaiveDate::from_ymd(2022, 4, 23);
            for _ in 0..10 {
                let mut st = String::new();
                let g = rand::thread_rng().gen();
                st.push(g);

                let size = rand::thread_rng().gen();

                let id = new_meet_rooms(db, size, &st).await?;
                ids.push(id);

                let rid = new_pub_room(db, id, date, irange).await?;

                new_booking(db, cid, rid, ibook).await?;
                new_booking(db, cid, rid, (book >> 20) as i64).await?;
            }
        }

        Ok(())
    }

    async fn down(&self, _: &SchemaManager) -> Result<(), DbErr> {
        Ok(())
    }
}

fn pwd_encoder(raw: &str) -> String {
    bcrypt::hash(raw, DEFAULT_COST).expect("加密密码错误")
}

async fn new_auth<Db: ConnectionTrait>(db: &Db, pwd: &str, name: &str) -> Result<i64, DbErr> {
    let auth = auth::ActiveModel {
        user_name: Set(name.to_string()),
        pwd: Set(pwd_encoder(pwd)),
        ..Default::default()
    };

    let id = auth::Entity::insert(auth).exec(db).await?.last_insert_id;
    Ok(id)
}

async fn new_admin<Db: ConnectionTrait>(db: &Db, pwd: &str, name: &str) -> Result<(), DbErr> {
    let admin = admin::ActiveModel {
        auth_id: Set(new_auth(db, pwd, name).await?),
        ..Default::default()
    };
    admin::Entity::insert(admin).exec(db).await?;
    Ok(())
}

async fn new_custom<Db: ConnectionTrait>(db: &Db, pwd: &str, name: &str) -> Result<i64, DbErr> {
    let admin = custom::ActiveModel {
        auth_id: Set(new_auth(db, pwd, name).await?),
        ..Default::default()
    };
    let id = custom::Entity::insert(admin).exec(db).await?.last_insert_id;
    Ok(id)
}

async fn new_meet_rooms<Db: ConnectionTrait>(
    db: &Db,
    number: u32,
    name: &str,
) -> Result<i64, DbErr> {
    let room = meeting_room::ActiveModel {
        name: Set(name.to_string()),
        max_people_number: Set(number),
        ..Default::default()
    };

    Ok(meeting_room::Entity::insert(room)
        .exec(db)
        .await?
        .last_insert_id)
}

async fn new_pub_room<Db: ConnectionTrait>(
    db: &Db,
    room_id: i64,
    data: NaiveDate,
    ban: i64,
) -> Result<i64, DbErr> {
    let room_pub = booking_status::ActiveModel {
        room_id: Set(room_id),
        day: Set(data),
        status: Set(ban),
        ..Default::default()
    };

    let stmt = booking_status::Entity::insert(room_pub)
        .exec(db)
        .await?
        .last_insert_id;

    Ok(stmt)
}

async fn new_booking<Db: ConnectionTrait>(
    db: &Db,
    uid: i64,
    pub_id: i64,
    time: i64,
) -> Result<(), DbErr> {
    let book = booking::ActiveModel {
        book_day_id: Set(pub_id),
        custom_id: Set(uid),
        time_pice: Set(time),
        ..Default::default()
    };

    booking::Entity::insert(book).exec(db).await?;
    Ok(())
}
